/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301, USA.
 * */

// AmzServer - Tototoy's server model.
// Author: tonyjobmails@gmail.com (tonyhack).
//
// Define class SortQueueBufferDynamic.
// This SortQueueBufferDynamic gives a sort queue and minimizes the count of
// memory allocation.
//

#ifndef __SORT__QUEUE__BUFFER__DYNAMIC__H
#define __SORT__QUEUE__BUFFER__DYNAMIC__H

#include <map>

#include "byte_buffer_dynamic.h"
#include "execute_callback.h"
#include "condition.h"

namespace amz {

// There T should support operate =.
template <typename T>
class SortQueueBufferDynamic {
  // <position, size>
  typedef std::pair<uint32, uint32> ElementBase;
  typedef std::pair<ElementBase, T> ElementInformation;
  // <serial number, ElementInformation>
  typedef std::map<uint32, ElementInformation> ElementIndex;
  typedef typename ElementIndex::iterator ElementIndexIterator;

 public:
  // <size, data>
  typedef typename std::pair<uint32, const char *> ExecuteElementPair;
  typedef typename std::pair<T *, ExecuteElementPair> ExecuteElement;

  explicit SortQueueBufferDynamic(uint32 max_unit_size);
  virtual ~SortQueueBufferDynamic();

  // Write.
  // Return the size of wirte.
  // < 0: error(-1: exist serial number, -2: memory allocation error.)
  int Write(uint32 serial_number, const char *data, int size, const T &sub_message);

  // Read.
  int Read(uint32 serial_number, char *data, int max_size, T &sub_message);

  // Remove.
  int Remove(uint32 serial_number, char *data, int max_size, T &sub_message);
  int RemoveNext(char *data, int max_size, T &sub_message);

  // Modify the sub mesasge.
  bool Modify(uint32 serial_number, const T &sub_message);
  // Delete.
  bool Delete(uint32 serial_number);
  void DeleteIf(Condition<T> *condition);

  // Execute.
  void Execute(ExecuteCallback<ExecuteElement> *callback);
  void ConditionExecute(Condition<T> *condition,
         ExecuteCallback<ExecuteElement> *callback);

 private:
  ByteBufferDynamic buffer_;
  ElementIndex query_index_;
};


template <typename T>
SortQueueBufferDynamic<T>::SortQueueBufferDynamic(uint32 max_unit_size)
       : buffer_(max_unit_size) {}


template <typename T>
SortQueueBufferDynamic<T>::~SortQueueBufferDynamic() {}


template <typename T>
int SortQueueBufferDynamic<T>::Write(uint32 serial_number, const char *data,
       int size, const T &sub_message) {
  if(this->query_index_.find(serial_number) != this->query_index_.end()) {
    return -1;
  }
  if(this->buffer_.PreWrite(size) == false) {
    LOG(ERROR) << "Failed to Write, buffer_ size is not enough!";
    return -2;
  }
  // Position, size, and sub_message.
  ElementInformation information;
  information.first.first = this->buffer_.WriteIndex();
  information.first.second = size;
  information.second = sub_message;
  // Write .
  memcpy(this->buffer_.WritePointer(), data, size);
  this->buffer_.WriteShift(size);
  // Insert into query index.
  this->query_index_.insert(std::make_pair(serial_number, information));

  return size;
}


template <typename T>
int SortQueueBufferDynamic<T>::Remove(uint32 serial_number, char *data,
       int max_size, T &sub_message) {
  ElementIndexIterator iterator = this->query_index_.find(serial_number);
  if(iterator == this->query_index_.end()) {
    return 0;
  }
  ElementInformation &information = iterator->second;
  uint32 data_position = information.first.first;
  uint32 data_size = information.first.second;
  if(data_size > (uint32)max_size) {
    return 0;
  }
  memcpy(data, this->buffer_.RandomReadPointer(data_position), data_size);
  this->buffer_.RandomReadShift(data_position, data_size);
  sub_message = information.second;
  // Update other data's position.
  uint32 *updata_position = NULL;
  ElementIndexIterator iterator_pos = iterator++;
  for(; iterator != this->query_index_.end(); ++iterator) {
    updata_position = &(iterator->second.first.first);
    if(*updata_position > data_position) {
      *updata_position -= data_size;
    }
  }
  // Erase from queue index.
  this->query_index_.erase(iterator_pos);
  return data_size;
}


template <typename T>
int SortQueueBufferDynamic<T>::RemoveNext(char *data, int max_size, T &sub_message) {
  ElementIndexIterator iterator = this->query_index_.begin();
  if(iterator == this->query_index_.end()) {
    return 0;
  }
  ElementInformation &information = iterator->second;
  uint32 data_position = information.first.first;
  uint32 data_size = information.first.second;
  if(data_size > max_size) {
    return 0;
  }
  memcpy(data, this->buffer_.RandomReadPointer(data_position), data_size);
  this->buffer_.RandomReadShift(data_position, data_size);
  sub_message = information.second;
  // Update other data's position.
  uint32 *updata_position = NULL;
  ElementIndexIterator iterator_pos = iterator++;
  for(; iterator != this->query_index_.end(); ++iterator) {
    iterator->second.first.first -= data_size;
  }
  // Erase from queue index.
  this->query_index_.erase(iterator_pos);
  return data_size;
}


template <typename T>
int SortQueueBufferDynamic<T>::Read(uint32 serial_number, char *data,
       int max_size, T &sub_message) {
  ElementIndexIterator iterator = this->query_index_.find(serial_number);
  if(iterator == this->query_index_.end()) {
    return 0;
  }
  ElementInformation &information = iterator->second;
  uint32 data_size = information.first.second;
  if(data_size > max_size) {
    return 0;
  }
  memcpy(data, this->buffer_.RandomReadPointer(information.first.first), data_size);
  sub_message = information.second;
  return data_size;
}


template <typename T>
bool SortQueueBufferDynamic<T>::Modify(uint32 serial_number, const T &sub_message) {
  ElementIndexIterator iterator = this->query_index_.find(serial_number);
  if(iterator == this->query_index_.end()) {
    return false;
  }
  iterator->second.second = sub_message;
  return true;
}


template <typename T>
bool SortQueueBufferDynamic<T>::Delete(uint32 serial_number) {
  ElementIndexIterator iterator = this->query_index_.find(serial_number);
  if(iterator == this->query_index_.end()) {
    return false;
  }
  ElementInformation &information = iterator->second;
  uint32 data_position = information.first.first;
  uint32 data_size = information.first.second;
  this->buffer_.RandomReadShift(data_position, data_size);
  // Update other data's position.
  uint32 *updata_position = NULL;
  ElementIndexIterator iterator_pos = iterator++;
  for(; iterator != this->query_index_.end(); ++iterator) {
    updata_position = &(iterator->second.first.first);
    if(*updata_position > data_position) {
      *updata_position -= data_size;
    }
  }
  // Erase from queue index.
  this->query_index_.erase(iterator_pos);
  return true;
}


template <typename T>
void SortQueueBufferDynamic<T>::DeleteIf(Condition<T> *condition) {
  ElementIndexIterator iterator = this->query_index_.begin();
  for(; iterator != this->query_index_.end(); ++ iterator) 
    if(condition->Check(iterator->second.second)) {
      ElementInformation &information = iterator->second;
      uint32 data_position = information.first.first;
      uint32 data_size = information.first.second;
      this->buffer_.RandomReadShift(data_position, data_size);
      // Update other data's position.
      uint32 *updata_position = NULL;
      ElementIndexIterator iterator_pos = iterator++;
      for(; iterator != this->query_index_.end(); ++iterator) {
        updata_position = &(iterator->second.first.first);
        if(*updata_position > data_position) {
          *updata_position -= data_size;
        }
      }
      iterator = iterator_pos;
      // TODO: Compile error, why?
      // iterator = this->query_index_.erase(iterator_pos);
      this->query_index_.erase(iterator_pos);
    }
}


template <typename T>
void SortQueueBufferDynamic<T>::Execute(ExecuteCallback<ExecuteElement> *callback) {
  ElementIndexIterator iterator = this->query_index_.begin();
  for(; iterator != this->query_index_.end();) {
    ElementInformation &element = iterator->second;
    ExecuteElementPair pair = std::make_pair(element.first.second,
           this->buffer_.RandomReadPointer(element.first.first));
    ExecuteElement information = std::make_pair(&element.second, pair);
    callback->OnExecute(information);
  }
}


template <typename T>
void SortQueueBufferDynamic<T>::ConditionExecute(Condition<T> *condition,
       ExecuteCallback<ExecuteElement> *callback) {
  ElementIndexIterator iterator = this->query_index_.begin();
  for(; iterator != this->query_index_.end();) {
    ElementInformation &element = iterator->second;
    if(condition->Check(element.second)) {
      ExecuteElementPair pair = std::make_pair(element.first.second,
             this->buffer_.RandomReadPointer(element.first.first));
      ExecuteElement information = std::make_pair(&element.second, pair);
      callback->OnExecute(information);
    }
  }
}

}  // namespace amz

#endif  // __SORT__QUEUE__BUFFER__DYNAMIC__H

